//////////////////////////////////////////////////////////////////////////////
//
// Copyright(C) < 1998 - 2024 > Glodon Company Limited
// Copyright 2003-2023 by Autodesk, Inc. 
//
// Licensed under the MIT License
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
//////////////////////////////////////////////////////////////////////////////

#if Gdmp || Gnuf
using Glodon.Gdmp.DB;
using Glodon.Gdmp.UI;
using Glodon.Gdmp.Exceptions;

using Application = Glodon.Gdmp.DB.Application;

#elif Gap
using Glodon.Gap.DB;
using Glodon.Gap.UI;
using Glodon.Gap.Exceptions;
using Application = Glodon.Gap.DB.Application;
#endif
using Glodon.Lookup.Core;
using Glodon.Lookup.Lookup.Core;
using Glodon.Lookup.Models;
using Glodon.Lookup.Services.Contracts;
using Glodon.Lookup.Services.Enums;
using Glodon.Lookup.ViewModels.Contracts;
using Glodon.Lookup.ViewModels.Pages.Tracebale;
using Glodon.Lookup.Core.Contracts;
using Glodon.Lookup.Core.Utils;
using Glodon.Lookup.Core.ModelBase;

using Wpf.Ui.Common;
using Wpf.Ui.Contracts;
using Wpf.Ui.Controls;

using System.Runtime.InteropServices;

using OperationCanceledException = System.OperationCanceledException;

using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;

using System.Windows;

using Glodon.Lookup.ViewModels.Objects;

namespace Glodon.Lookup.ViewModels.Pages
{
    public sealed partial class SnoopViewModel : SnoopViewModelBase, ITabGroupViewModel<SnoopHistoryViewModel>, IApiEventHandler
    {
        private readonly ISnackbarService _snackbarService;

        public SearchBoxMediator SearchBoxMediator { get; set; }
        public IList<SnoopHistoryViewModel> TabData { get; set; }

        [ObservableProperty]
        private Visibility _isClearCommandShow;

        [ObservableProperty]
        private SnoopHistoryViewModel _currentViewModel;

        partial void OnCurrentViewModelChanged(SnoopHistoryViewModel value)
        {
            OnSearchTextChanged(SearchBoxMediator.SearchText);
        }

        [RelayCommand]
        private void ClearSearchText()
        {
            SearchBoxMediator.SearchText = string.Empty;
        }
        public SnoopViewModel(ISnackbarService snackbarService)
            : base(snackbarService)
        {
            SearchBoxMediator = new SearchBoxMediator();
            SearchBoxMediator.SearchTextChangedAction += OnSearchTextChanged;

            _snackbarService = snackbarService;
            TabData = new List<SnoopHistoryViewModel>
            {
                new SnoopHistoryViewModel(SearchBoxMediator) { Tab = DataTabType.Document },
                new SnoopHistoryViewModel(SearchBoxMediator) { Tab = DataTabType.ModelView },
                new SnoopHistoryViewModel(SearchBoxMediator) { Tab = DataTabType.Selection }
            };
            GlodonAPI.DocumentFileName = GlodonAPI.Document!.PathName;
            CurrentViewModel = TabData.First();
            IsClearCommandShow = Visibility.Hidden;
        }


        /// <summary>
        /// 判断图元是否存在。
        /// </summary>
        private bool ElementNotExisted(Element element)
        {
            try
            {
                var latestEle = GlodonAPI.Document.GetElement(element.Id);
                if (latestEle == null)
                {
                    return true;
                }
                return false;
            }
            catch (Exception e)
            {
                return true;
            }
        }

        public override void Snoop(SnoopableObject snoopableObject)
        {
            CurrentViewModel.SnoopableData = Array.Empty<Descriptor>();
            SupressTreeSelectedChangedCommand = true;
            SearchBoxMediator.SearchText = string.Empty;
            if (snoopableObject.Descriptor is IDescriptorEnumerator descriptor && descriptor.IsEmpty == false)
                try
                {
                    IReadOnlyList<SnoopableObject> snoopableObjects = descriptor.ParseEnumerable(snoopableObject);
                    CurrentViewModel.AddHistory(snoopableObjects);
                }
                catch (Exception exception)
                {
                    SnowException("Invalid object", exception.Message);
                }
            else
            {
                CurrentViewModel.AddHistory(new[] { snoopableObject });
            }
            SupressTreeSelectedChangedCommand = false;
        }

        public override async Task Snoop(DataTabType tab)
        {
            try
            {
                IReadOnlyCollection<SnoopableObject> snoopableObjects = Selector.Snoop(tab);
                SnoopHistoryViewModel snoopHistoryViewModel = GetVmByTab(tab);
                if (tab == DataTabType.Selection)
                {
                    snoopHistoryViewModel.ClearHistory();
                }
                snoopHistoryViewModel.AddHistory(snoopableObjects);

                //刷新详情页。
                _ = FetchMembersCommand.ExecuteAsync(null);

            }
            catch (OperationCanceledException exception)
            {
                _snackbarService.Show("操作取消", exception.Message, SymbolRegular.Warning24, ControlAppearance.Caution);
            }
            catch (Exception exception)
            {
                _ = _snackbarService.ShowAsync("数据查找出错", exception.Message, SymbolRegular.ErrorCircle24, ControlAppearance.Danger);
            }
        }
        public override async Task CollectMembers(bool useCached)
        {
            if (CurrentViewModel.SnoopableObjects != null && !CurrentViewModel.SnoopableObjects.Any(sn => sn.IsSelected))
            {
                CurrentViewModel.SnoopableData = Array.Empty<Descriptor>();
                return;
            }

            try
            {
                if (CurrentViewModel is { SnoopHistory: { Count: > 0 } })
                {
                    SnoopableObject selectedSnObj = CurrentViewModel.SnoopHistory.Last().SelectedObject;
                    CurrentViewModel.SnoopableData = useCached ? await selectedSnObj.GetCachedMembersAsync() : await selectedSnObj.GetMembersAsync();
                }
            }
            catch (InvalidObjectException exception)
            {
                SnowException("无效对象", exception.Message);
            }
            catch (InternalException)
            {
                const string message = "API报告的问题，通常由于目标API对象已无效或者内存被释放";
                SnowException("API内部错误", message);
            }
            catch (SEHException)
            {
                const string message = "API报告的问题，通常由于目标API对象已无效或者内存被释放";
                SnowException("API内部错误", message);
            }
            catch (Exception exception)
            {
                SnowException("数据查找出错", exception.Message);
            }
        }

        protected override void OnTreeSelectedChanged(SnoopableObject newObj)
        {
            if (CurrentViewModel.SnoopHistory is { Count: > 0 })
            {
                CurrentViewModel.SnoopHistory.Last().CachedSelectedObject = newObj;
            }

            if (CurrentViewModel is { SnoopHistory: { Count: > 0 } })
            {
                CurrentViewModel.SnoopHistory.Last().RaiseSelectedObjectChanged();
            }
        }

        public void ClearData(DataTabType dataTab = DataTabType.None)
        {
            SearchBoxMediator.SearchText = string.Empty;
            if (dataTab == DataTabType.None)
            {
                CurrentViewModel = TabData.First(vm => vm.Tab == DataTabType.Document);
                foreach (var vm in TabData)
                {
                    vm.ResetHistoryCommand.Execute(0);
                }
            }
            else
            {

                GetVmByTab(dataTab).ResetHistoryCommand.Execute(0);
            }
        }

        public SnoopHistoryViewModel GetVmByTab(DataTabType dataTab)
        {
            return TabData.FirstOrDefault(vm => vm.Tab == dataTab);
        }

        private void OnApplicationIdling_RefreshData(object sender, ApplicationIdlingEventArgs args)
        {
            foreach (var vm in TabData)
            {
                vm.ResetHistoryCommand.Execute(0);
                _ = Snoop(vm.Tab);
            }
            UIApplication.Get().Idling -= OnApplicationIdling_RefreshData;
        }

        private void OnApplicationIdling_ClearData(object sender, ApplicationIdlingEventArgs args)
        {
            if (Application.Get().ActiveDocument == null)
            {
                ExitCommand.Execute(null);
            }
            UIApplication.Get().Idling -= OnApplicationIdling_ClearData;
        }

        // 文档修改时用来刷新ModelView首页数据（DocumentChanged 里数据不完整）
        private void OnApplicationIdling_RefreshModelView(object sender, ApplicationIdlingEventArgs args)
        {
            GetVmByTab(DataTabType.ModelView).UpdateHome();
            //使用一次后取消注册
            UIApplication.Get().Idling -= OnApplicationIdling_RefreshModelView;
        }

        // 文档选择改变时用来刷新首页数据（SelectionChanged 里数据不完整）
        private void OnApplicationIdling_RefreshHome(object sender, ApplicationIdlingEventArgs args)
        {
            GetVmByTab(DataTabType.Document).UpdateHome();
            GetVmByTab(DataTabType.ModelView).UpdateHome();
            //使用一次后取消注册
            UIApplication.Get().Idling -= OnApplicationIdling_RefreshHome;
        }

        private void UpdateSnoopableObjects(IList<ElementId> added, IList<ElementId> deleted, IList<ElementId> modified)
        {
            try
            {
                //更新文档Tab首页的数据
                if (GetVmByTab(DataTabType.Document).SnoopHistory.Any())
                {
                    SnoopAction homeData = GetVmByTab(DataTabType.Document).SnoopHistory.First();
                    var docHomeObjects = homeData.Objects.ToList();
                    docHomeObjects.RemoveAll(s => s.Object is Element e && !e.IsValidObject());
                    //var docHomeInvalidObjs = docHomeObjects.Where(s => s.Object is Element e && !e.IsValidObject());
                    foreach (var item in added)
                    {
                        var element = GlodonAPI.Document.GetElement(item);
                        docHomeObjects.Add(new SnoopableObject(GlodonAPI.Document, element));
                    }
                    foreach (var item in modified)
                    {
                        if (docHomeObjects.Any(sn => sn.Object is Element e && e.IsValidObject() && e.Id.Equals(item)))
                        {
                            var element = GlodonAPI.Document.GetElement(item);
                            SnoopableObject modifiedObject = docHomeObjects.First(sn => sn.Object is Element e && e.IsValidObject() && e.Id.Equals(item));
                            modifiedObject.Object = element;
                            modifiedObject.ClearMembersCache();
                        }
                    }
                    GetVmByTab(DataTabType.Document).SnoopHistory.First().Objects = docHomeObjects;
                }
                //更新Selection首页数据
                if (GetVmByTab(DataTabType.Selection).SnoopHistory.Any())
                {
                    SnoopAction homeData = GetVmByTab(DataTabType.Selection).SnoopHistory.First();
                    var docHomeObjects = homeData.Objects.ToList();
                    foreach (var item in modified)
                    {
                        if (docHomeObjects.Any(sn => sn.Object is Element e && e.IsValidObject() && e.Id.Equals(item)))
                        {
                            var element = GlodonAPI.Document.GetElement(item);
                            SnoopableObject modifiedObject = docHomeObjects.First(sn => sn.Object is Element e && e.IsValidObject() && e.Id.Equals(item));
                            modifiedObject.Object = element;
                            modifiedObject.ClearMembersCache();
                        }
                    }
                }
                //如果选中元素被修改或者无效了，重置历史
                foreach (var vm in TabData)
                {
                    if(vm.SnoopHistory.Any())
                    {
                        if (vm.SnoopHistory.FirstOrDefault().SelectedObject is { Object: { } objSel } && objSel is Element SelectedEle)
                        {
                            if (!SelectedEle.IsValidObject() || modified.Any(mid => mid.Equals(SelectedEle.Id)))
                            {
                                vm.ResetHistoryCommand.Execute(1);
                            }
                        }
                    }
                }
                if (CurrentViewModel.Tab == DataTabType.Document && GetVmByTab(DataTabType.Document).SnoopHistory.Count == 1)
                {
                    //刷新文档视图
                    CurrentViewModel.RaiseSnoopableObjectsChanged();
                }
                //空闲时再刷新 ModelView 数据
                UIApplication.Get().Idling += OnApplicationIdling_RefreshModelView;

            }
            catch (Exception e)
            {
                _snackbarService.ShowAsync(e.Message + e.StackTrace, "更新数据时出错");
                return;
            }
        }
        public void OnUIViewActivated(object sender, UIViewActivatedEventArgs e)
        {
            try
            {
                if (GlodonAPI.DocumentFileName == null)
                {
                    GlodonAPI.DocumentFileName = GlodonAPI.Document!.PathName;
                }
                if (GlodonAPI.DocumentFileName != GlodonAPI.Document!.PathName)
                {//当前文档改变
                    if (GetVmByTab(DataTabType.Document).SnoopableObjects.Any(sn => sn.Object is Document))
                    {
                        var originalDoc = GetVmByTab(DataTabType.Document).SnoopableObjects.First(sn => sn.Object is Document).Object as Document;
                        if (originalDoc != null)
                        {
                            //解除旧文档对象注册的事件处理器，注册到新文档对象
                            GlodonAPI.UpdateHandlerDocument(originalDoc, this);
                        }
                    }
                    //刷新数据
                    foreach (var vm in TabData)
                    {
                        vm.ResetHistoryCommand.Execute(1);
                        vm.UpdateHome();
                    }
                    //更新当前文档文件名
                    GlodonAPI.DocumentFileName = GlodonAPI.Document!.PathName;
                }
                else
                {
                var modelViewVm = GetVmByTab(DataTabType.ModelView);
                if (modelViewVm != null)
                {
                    var currentModelView = GlodonAPI.Document!.GetElement(e.CurrentActiveView.ModelViewId) as ModelView;
                    if (currentModelView != null)
                    {
                        bool needReset = false;
                        SnoopableObject selectedObject = modelViewVm.SnoopHistory.First().SelectedObject;
                        if (selectedObject != null)
                        {
                            bool selectedIsModelView = selectedObject.Object is ModelView;
                                var activeView = GlodonAPI.ActiveView;
                                bool selectedNotExsited = selectedObject.Object is Element ele && (!ele.IsValidObject() || !activeView!.GetElementVisibility(ele.Id));

                            //如果有深入查看的历史，并且查看对象为ModelView或者在新视图中不可见的对象，需要重置历史
                            //（因为所查看的ModelView对象与当前视图不再匹配）
                            needReset = modelViewVm.SnoopHistory.Count > 1 && (selectedIsModelView || selectedNotExsited);
                        }

                        if (needReset)
                        {
                            modelViewVm.ResetHistoryCommand.Execute(1);
                        }
                        modelViewVm.UpdateHome();
                    }
                }
            }
            }
            catch (Exception ex)
            {
                _snackbarService.ShowAsync("视图已激活事件处理错误", ex.Message + ex.StackTrace, SymbolRegular.ErrorCircle16, ControlAppearance.Danger);
            }
        }
        /// <summary>
        /// 文档内容修改事件处理函数
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        public void OnDocumentChanged(object sender, DocumentChangedEventArgs e)
        {
            IList<ElementId> added = e.GetAddedElementIds();
            IList<ElementId> deleted = e.GetDeletedElementIds();
            IList<ElementId> modified = e.GetModifiedElementIds();
            UpdateSnoopableObjects(added, deleted, modified);
        }
        public void OnDocumentOpened(object sender, DocumentOpenedEventArgs e)
        {
            if (e.Document.IsFamilyDocument) return;
            UIApplication.Get().Idling += OnApplicationIdling_RefreshData;
        }
        public void OnDocumentClosed(object sender, DocumentClosedEventArgs e)
        {
            if (e.Document.IsFamilyDocument) return;
            UIApplication.Get().Idling += OnApplicationIdling_ClearData;

        }
        public void OnDocumentSelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            //当前视图为选择视图，直接刷新
            if (CurrentViewModel.Tab == DataTabType.Selection)
            {
                //清空搜索词，因为当搜索词为id时，即使用户切换选择，界面依然显示搜索结果，影响用户查看选择内容
                SearchBoxMediator.SearchText = string.Empty;
                _ = Snoop(DataTabType.Selection);
            }
            else
            {
                //当前视图不是选择视图，但需要重置选择视图历史,以避免切换至选择视图时获取无效历史
                //(因为切换视图时默认获取历史，但选择视图历史存在时效性，选择改变发生时不再有效)
                GetVmByTab(DataTabType.Selection).UpdateHome();
                GetVmByTab(DataTabType.Selection).ResetHistoryCommand.Execute(1);
            }

            //注册一次性空闲事件刷新因选择构件产生的辅助对象数据，选择改变事件中获取不到
            UIApplication.Get().Idling += OnApplicationIdling_RefreshHome;
        }


        private void OnSearchTextChanged(string value)
        {
            IsClearCommandShow = string.IsNullOrEmpty(value) || string.IsNullOrWhiteSpace(value) ? Visibility.Hidden : Visibility.Visible;
            if (CurrentViewModel.SnoopableViewSource.View != null)
            {
                CurrentViewModel.SnoopableViewSource.View.Refresh();
                CurrentViewModel.TrySelectFirst(false);
            }
        }
    }
}
